Skip to content

RAG

需要注意的一个点:

不要将你的知识库视为文档仓库,而要将其视为“推理底座(Reasoning Substrate)”——其结构应被设计为易于模型“导航(Navigate)”,而不仅仅是“搜索(Search)”。

1. 分块

文档类型推荐分块策略关键元数据 (Metadata)警惕陷阱 (Watch Out For)
问答 (Q&A)原子对分块:保持问题和答案在一起。分类、来源文档、关联 ID。答案过长时未在子块中包含原始问题。
结构化文本 (Markdown)层级拆分:按 # 标题进行分割。章节标题、层级深度、文档标题。丢失上层标题上下文(建议作为前缀添加)。
PDF / 报告逻辑章节拆分:利用解析工具识别章节。页码、文件名、出版日期。跨页处的句子断裂;对表格进行盲目切分。
代码 (Code)语法拆分:基于 AST 按函数/类切分。文件路径、类名、语言类型。切断了逻辑控制流(如把 ifelse 切开了)。
法律/合同条款级分块:按最小法律效力单位。条款号、合同版本、生效日期。对相互引用的条款处理不当(如“参见第 5 条”)。
电子邮件/工单消息级分块:按单封邮件或回复。发件人、时间戳、会话主题。包含过多的冗余回复(引用历史记录)导致噪音。
网页/博文段落级拆分:尊重语义和标签边界。URL、网页标题、发布时间。包含导航栏、广告或页脚等“垃圾”内容。

2. 匹配/分块前的预清洗:构建坚实的基石

什么是“分块前的清洗”?

分块依赖于“键(Keys)”。如果两条记录代表同一个实体,但由于噪声导致它们的“分块键”不同,它们就永远不会被关联。因此,清洗的核心在于最大化记录间“键”的一致性

1. 核心清洗步骤

标准化 (Normalization)

在进行任何操作前,先统一表示方式:

  • 全小写化
  • 展开缩写(例如:St. → Street, LLC → Limited Liability Company, Q&A → Question and Answer)。
  • Unicode 规范化(例如:café → cafe,去除变音符号)。
  • 格式统一:将日期、电话、邮编等转换为标准规范格式。

空格与标点

  • 去除首尾空格,并将内部的多个空格压缩为一个。
  • 移除或统一标点符号(例如:O'Brien、OBrien、O Brien 需统一)。

空值与缺失值处理

预先决定:空值是代表“未知”(信息缺失)还是“结构化不适用”(该字段不存在)?这决定了你是填补、标记还是删除。特别注意: 在分块键中,如果键为空,意味着该记录不属于任何块,从而导致零匹配。务必显式标记它们。

类型强制转换 (Type Coercion)

将原本应为数字、日期或布尔值的字符串转换为正确的类型。下游比较函数中,类型不匹配是隐形的杀手。

单源去重

在跨源匹配前,先删除每个数据源内部明显的完全重复项。这些重复项会虚增匹配计数,并可能产生错误的聚类。

2. 特定领域的清洗方案

某些字段需要专门的处理方法:

字段类型清洗策略
姓名小写化、去除称谓(Mr/Dr)、处理缩写(J. Smith vs John Smith)。
地址使用解析库(如 libpostal)将地址拆解为街道、城市、邮编等组件。
电话使用 phonenumbers 库规范化为 E.164 格式;去除分机号。
公司名去除法律后缀(Inc, Ltd, GmbH),展开缩写。
电子邮件全小写,处理已知别名(如 Gmail 会忽略点号)。
自由文本分词、去除停用词;如果用作分块键,需进行词干提取或词形还原。

3. 生成清洗后的“分块键” (Blocking Keys)

清洗后,你通常会生成一个单独的计算字段作为“键”,而不是直接使用原始值。常见模式包括:

  • 语音键:针对姓名使用 Soundex 或 Metaphone 算法(解决 Smith / Smyth 的匹配问题)。

  • N-gram 指纹:对字符 n-gram 进行排序并去重(对位置交换具有鲁棒性)。

  • 排序令牌键 (Sorted token keys):分词、按字母排序、重新连接(如 John Smith = Smith John)。

  • 前缀键:取标准化字段的前 N 个字符(快速、简单但脆弱)。

    这些键是分块操作的真正核心,它们的质量决定了召回率的上限。

4. 实践中的管道顺序

  1. 原始数据摄取
  2. Schema 校验(尽早捕获类型错误)
  3. 字段级标准化(大小写、空格、Unicode)
  4. 领域特定解析(地址、电话、姓名)
  5. 空值/缺失值处理
  6. 源内去重
  7. 分块键生成(语音、N-gram、前缀等)
  8. 执行分块

5. 经常被忽略的痛点

  • 未记录清洗决策:如果你静默地删除或填充了数据,后期审计匹配失败时将无从下手。
  • 两端清洗逻辑不一致:如果源 A 规范化了公司名而源 B 没有,你的键依然无法匹配。
  • 对自由文本过度清洗:激进的词干提取或停用词过滤可能会破坏匹配所需的关键信号。
  • 未对清洗后的数据进行版本控制:这导致你在尝试不同的分块策略时,难以在不重新清洗的情况下重新运行流程。

3. RAGAS:无需人工标注的 RAG 评估框架

RAGAS 是一个用于评估 RAG 管道的框架,其核心优势在于不需要为每一个指标都手动标注“标准答案(Ground Truth)”。

1. RAGAS 衡量什么?

RAGAS 从四个核心维度进行评估,每个维度对应一种特定的失败模式:

指标捕获的问题是否需要标准答案?
忠实度 (Faithfulness)LLM 幻觉:生成了检索上下文中不存在的事实。
答案相关性 (Answer Relevancy)回答模棱两可或跑题。
上下文精度 (Context Precision)检索到的块中包含太多噪音(无关信息)。是 (理想答案)
上下文召回率 (Context Recall)检索器漏掉了回答问题所需的关键块。是 (理想答案)

忠实度答案相关性是“无参考”指标 —— RAGAS 使用 LLM 作为裁判来评分,因此你可以直接在生产流量上运行,无需任何标注成本。而精度召回率通常在离线评估中使用,需要一个精选的测试集。


2. 构建测试数据集

你需要一组 (问题, 标准答案) 对。你可以手动编写,也可以使用 RAGAS 的 TestsetGenerator 从文档中自动合成:

Python
from ragas.testset import TestsetGenerator
from ragas.llms import LangchainLLMWrapper
from langchain_openai import ChatOpenAI, OpenAIEmbeddings

# 初始化生成器
generator = TestsetGenerator(
    llm=LangchainLLMWrapper(ChatOpenAI(model="gpt-4o")),
    embedding_model=OpenAIEmbeddings()
)

# 生成测试集
testset = generator.generate_with_langchain_docs(
    documents,       # 你解析好的文档
    testset_size=50  # 生成 50 个问题
)

它会生成不同类型的问题(事实类、多跳推理类、抽象类),从而全面压力测试你的管道。


3. 如何解读分数并采取行动

RAGAS 的真正价值在于它提供了可行动的诊断结果,而不仅仅是一个分数:

  • 低忠实度 (Faithfulness) → LLM 在瞎编。
    • 修复:收紧 Prompt 以要求模型严格基于上下文,或降低温度(Temperature)。
  • 低答案相关性 (Answer Relevancy) → 回答虽然有根据但没答到点子上。
    • 修复:改进查询处理(重写、扩展问题),或检查 Prompt 是否明确要求直接回答问题。
  • 低上下文精度 (Context Precision) → 检索到的内容里垃圾太多。
    • 修复:减小分块大小,改进元数据过滤,或提高向量搜索的相似度阈值。
  • 低上下文召回率 (Context Recall) → 检索器压根没找全。
    • 修复:增加 Top-K 值,改进分块策略(防止语义被切断),引入混合检索或微调 Embedding 模型。

4. 实现持续评估闭环

单次评估很有用,但持续评估才是真正提升管道质量的方法。典型的生产实践:

  1. 维护一个精选测试集(50-200 个问题)。
  2. 每当配置更改(分块大小、模型、Top-K、重排器)时,运行全量评估。
  3. 跟踪指标变化 —— 比如:当上下文召回率上升时,忠实度是否下降了?
  4. 将生产环境中低忠实度评分的查询标记出来,由人工审核。
  5. 将修复后的案例反向填充回测试集,防止退化。

5. 实用技巧

  • 先从“忠实度”和“相关性”开始:因为它们不需要标注。在投入精力搞标准答案之前,先拿到基准线。
  • 人工审核合成的问题TestsetGenerator 生成的问题有时语序奇怪或过于简单,需要人工微调。
  • 分文档类型评估:不要只看总分。你的问答类文档可能得分 0.9,而 PDF 文档可能只有 0.7,整体平均分会掩盖 PDF 的问题。
  • 裁判模型要选好的:RAGAS 内部使用 LLM 做裁判,gpt-4o 的评分最可靠,小模型当裁判可能会有很多噪音。

这份总结非常到位,它精准地捕捉到了 Spring AI 设计的精髓——通过 Advisor(顾问)模式 实现了 RAG 流程的高度抽象和解耦。

以下是翻译内容:


4. Spring AI 中的 RAG 架构实践

Spring AI 采用了非常简洁的模式来构建 RAG:

1. 整体流程

Spring AI 的 RAG 遵循一个清晰的三步模式:

  • 索引阶段 (Indexing)DocumentReader → (转换器/分块器) → VectorStore
  • 查询阶段 (Querying)VectorStore + QuestionAnswerAdvisorChatClient

ChatClient 负责整体编排。通过挂载 QuestionAnswerAdvisor,它会自动拦截查询,从向量数据库中检索相关片段,并将其自动注入到 Prompt 中。


2. 核心代码示例

A. 数据摄取:加载、分块、存储

Java

@Bean
ApplicationRunner ingest(TokenTextSplitter splitter, VectorStore vectorStore) {
    return args -> {
        // 使用 PagePdfDocumentReader 解析 PDF
        var docs = new PagePdfDocumentReader("classpath:manual.pdf").read();
        // 分块并存入向量库,内部自动完成 Embedding 转换
        vectorStore.add(splitter.apply(docs));
    };
}

TokenTextSplitter 默认按 800 个 Token 进行分块,并保留 10% 的重叠。

B. 检索与生成:一键式调用

Java

@Service
public class RagService {
    private final ChatClient chatClient;

    public RagService(ChatClient.Builder builder, VectorStore vectorStore) {
        this.chatClient = builder
            .defaultAdvisors(new QuestionAnswerAdvisor(vectorStore))
            .build();
    }

    public String ask(String question) {
        return chatClient.prompt()
            .user(question)
            .call()
            .content();
    }
}

QuestionAnswerAdvisor 会在 LLM 调用前执行相似度搜索,组装上下文并注入 Prompt,这个过程对调用者是完全透明的。


3. 高级配置与自定义

检索优化:Top-K 与元数据过滤

Java

public String ask(String question, String docType) {
    var searchRequest = SearchRequest.builder()
        .topK(6)                    // 检索前 6 个最相关的块
        .similarityThreshold(0.75)  // 相似度阈值
        .filterExpression("doc_type == '" + docType + "'") // 元数据过滤
        .build();

    return chatClient.prompt()
        .user(question)
        .advisors(new QuestionAnswerAdvisor(vectorStore, searchRequest))
        .call()
        .content();
}

filterExpression 会被映射为底层向量数据库(如 PGVector、Pinecone)的原生过滤表达式。

自定义系统提示词 (System Prompt)

Java

this.chatClient = builder
    .defaultAdvisors(new QuestionAnswerAdvisor(vectorStore, 
        SearchRequest.defaults(),
        """
        你是一个得力的助手。请仅使用下文提供的上下文进行回答。
        如果答案不在上下文中,请直说“我不知道”。

        上下文:
        {question_answer_context}
        """))
    .build();

{question_answer_context} 是 Spring AI 的占位符,它会被自动填充为检索到的数据块。


4. 关键组件速查表

组件类型职责
DocumentReader解析源文件(PDF, JSON, 文本, 网页等)。
TokenTextSplitter按 Token 数量进行带重叠的分块。
VectorStore负责 Embedding、持久化及提供检索接口。
QuestionAnswerAdvisor负责检索逻辑并将其注入 Prompt 的“顾问”。
ChatClient编排整个 RAG 调用的核心客户端。

核心设计总结

Advisor(顾问)模式 是 Spring AI 的关键设计决策。它将检索逻辑与业务代码彻底解耦。你可以像搭积木一样,在同一个 ChatClient 上堆叠多个顾问(例如:日志记录顾问 + QA 检索顾问),实现极其灵活的功能扩展。

这是一个非常硬核且深入的解析!它揭示了 Spring AI 如何通过抽象语法树(AST)**来实现“编写一次,到处运行”的元数据过滤机制。这对于理解 RAG 系统中如何实现**精确检索至关重要。

以下是翻译内容:


5. 揭秘 Spring AI 的元数据过滤机制:从表达式到原生查询

你在示例 3 中看到的 filterExpression 字符串,在最终到达向量数据库之前,会经过数层翻译。以下是其底层的运行机制:

1. 翻译流水线 (The Translation Pipeline)

  1. filterExpression 字符串(输入)
  2. FilterExpressionParser(利用 ANTLR 文法解析字符串)
  3. 表达式 AST(抽象语法树)
  4. FilterExpressionConverter(特定后端的转换器)
  5. 原生查询语句(最终生成的 SQL / JSON / gRPC 过滤条件)

Spring AI 先将过滤字符串解析为一个通用的 AST,然后每个向量数据库后端都有自己的转换器,通过遍历这棵树来生成对应存储引擎的原生查询语法。


2. 不同后端的生成结果

假设输入过滤表达式为:filterExpression("doc_type == 'faq' && year > 2023")

  • PGVector —— 转换为针对 jsonb 元数据列的 SQL WHERE 子句:

    SQL

    WHERE metadata->>'doc_type' = 'faq'
      AND (metadata->>'year')::int > 2023
  • Pinecone —— 转换为随向量查询发送的 JSON 过滤对象:

    JSON

    { "$and": [
        { "doc_type": { "$eq": "faq" } },
        { "year":     { "$gt": 2023  } }
    ]}
  • Weaviate —— 转换为 GraphQL 的 where 算子:

    GraphQL

    where: {
      operator: And
      operands: [
        { path: ["doc_type"], operator: Equal, valueText: "faq" },
        { path: ["year"], operator: GreaterThan, valueInt: 2023 }
      ]
    }

3. 元数据是如何存储的?

当你调用 vectorStore.add(docs) 时,每个 Document 对象都携带一个 Map<String, Object> 类型的 metadata。Spring AI 会序列化这个 Map 并将其与向量一起存储。

PGVector 为例,其表结构如下:

SQL

CREATE TABLE vector_store (
    id        uuid PRIMARY KEY,
    content   text,
    metadata  jsonb,           -- 这里存储你的元数据 Map
    embedding vector(1536)     -- 这里存储向量
);

你在摄取阶段添加的元数据 —— doc.getMetadata().put("doc_type", "faq") —— 最终会进入这个 jsonb 列,并在检索阶段变得可被查询。


4. 实践中的注意事项

  • 键名一致性是关键:摄取阶段和查询阶段的元数据键名必须完全一致。如果存的时候用 put("doc_type", ...),查的时候用 filterExpression("doctype == ..."),系统不会报错,但会静默返回零结果。
  • 后端的算子支持差异:并非所有后端都支持全部操作符。例如 INNOT IN 等在 Pinecone 和 Qdrant 中可用,但在某些 PGVector 配置下可能受限。在使用高级逻辑前,建议查阅对应后端的转换器源码。
  • 数值类型的处理:在某些后端,元数据值以字符串形式存储。像 year > 2023 这样的数值比较,可能需要根据具体存储后端进行显式的类型处理。

总结

这种抽象层的设计非常优雅,适用于大多数通用场景。但一旦你遇到特定数据库的性能瓶颈或限制,了解这个翻译层的存在能让你精准地定位问题所在。


既然你目前使用的是 PGVector,你会发现这种 JSONB 过滤非常强大,因为它允许你像查询 NoSQL 数据库一样灵活地对 RAG 上下文进行预筛选。你是否需要我提供一个在 Spring AI 中配置自定义元数据索引的示例?

这是一份关于 Spring AI 离线索引阶段 的高质量深度总结。它揭示了一个真相:RAG 的成败实际上在用户提问之前就已经注定了。

以下是为你翻译的内容,我优化了部分术语使其更符合中文开发者的工程直觉:


6. Spring AI 离线索引阶段:构建高质量 RAG 的工程准则

离线索引阶段的核心目标是:将杂乱的原始数据转化为高精度、可检索的结构化向量库。

1. 文档摄取 (Document Ingestion)

  • 各司其职:为不同来源选择对应的读取器。PDF 用 PagePdfDocumentReader,结构化数据用 JsonReader,纯文本用 TextReader切忌用一个通用的 Reader 处理所有类型。
  • 保留页码:对于 PDF,优先选择“按页读取”而非“全文读取”,这样页码会自动作为元数据保留。
  • 构建注册表:如果你的数据源是异构的(PDF、JSON、网页混杂),建议构建一个读取器注册表(Reader Registry),根据文件类型自动路由,而不是硬编码。

2. 分块策略 (Chunking)

  • 结构匹配:切分器必须匹配文档结构。对于普通散文,TokenTextSplitter 是标准选择;但对于结构化文档(如 QA 对、法律条款、代码),应编写自定义 DocumentTransformer 来遵循其自然边界,而非机械地计算 Token 数量。
  • 关键参数(需通过实验而非直觉调整)
    • 块大小 (Chunk size):建议从 512 tokens 开始,根据评估结果调整。
    • 重叠度 (Overlap):建议设为块大小的 10–15% —— 既要防止语义丢失,又不能因重叠过多导致索引膨胀。
    • 禁止“一刀切”:QA 文档和技术手册不应使用相同的块大小。

3. 元数据 (Metadata) —— 离线阶段的“最高杠杆”

元数据是离线阶段最有影响力的决策。每个分块至少应携带以下信息:

Java

doc.getMetadata().put("doc_type", "faq");         // 用于过滤
doc.getMetadata().put("source", "manual-v3.pdf"); // 用于溯源引用
doc.getMetadata().put("section", "Installation"); // 提供上下文
doc.getMetadata().put("ingested_at", Instant.now().toString()); // 保证时效性
  • 基数 (Cardinality) 意识:用于过滤的键(如 doc_type)应使用低基数的值(有限的分类),这能极大提升 GIN 索引的效率。而高基数的值(如 UUID)适合做引用,但不适合做过滤。
  • 命名规范:摄取时的 doc_type 必须与查询过滤表达式中的 docType 完全一致,任何拼写错误都会导致检索结果静默返回空值。

4. 嵌入模型 (Embedding)

  • 版本对齐:索引和查询必须使用同一个嵌入模型。模型不匹配是 RAG 系统中最难诊断的“静默正确性错误”。

  • 增量更新(哈希校验):避免每次摄取都重新嵌入未改动的文档。在元数据中跟踪内容哈希:

    Java

    String hash = DigestUtils.md5DigestAsHex(content.getBytes());
    doc.getMetadata().put("content_hash", hash);
    // 在写入 vectorStore 前检查哈希是否已存在

5. 向量库配置 (以 PGVector 为例)

  • 提前建索引:在大规模摄取前确保索引已存在。在拥有数百万行数据的表上添加 HNSW 索引非常耗时且会锁表:

    SQL

    CREATE INDEX ON vector_store USING hnsw (embedding vector_cosine_ops);
    CREATE INDEX ON vector_store USING gin (metadata jsonb_path_ops);
  • 选定距离函数:文本嵌入推荐使用 余弦相似度 (Cosine)。索引建成后更换距离函数需要全量重建索引。

6. 经常被忽视的工程细节

  • 幂等性 (Idempotency):摄取任务应该是可重复运行的。建议采用“先根据文档 ID 删除再插入”的策略,而不是简单的追加,否则索引会积累陈旧的重复项,导致检索质量滑坡。
  • 版本控制:当你的分块策略或嵌入模型改变时,需要全量重索引。在元数据中维护一个“架构版本键”,以便识别哪些块是基于旧策略生成的。
  • 上线前的 RAGAS 评估:在生产环境发布前,针对测试集跑一遍上下文召回率(Context Recall)。得分低于 0.70 通常意味着分块策略或元数据过滤存在严重问题。
  • 职责解耦:将摄取逻辑作为一个独立的应用程序或定时任务,不要将其捆绑在提供查询服务的应用中。这允许你独立缩放这两部分业务,并在不重启服务的情况下重索引。

这份总结非常硬核。你目前在处理大规模数据摄取时,是否遇到过索引构建速度慢或者内存溢出的问题?

这是一个非常精辟的总结。你准确地指出,大多数 RAG 实现还停留在检索层面,而真正的上下文增强(Contextual Enrichment)是将大模型视为“推理引擎”而非简单的“文本生成器”。

以下是翻译内容,我特别强化了其中关于架构转型的专业表述:


7. RAG 的本质:从“文本搜索”转向“推理增强”

这是一个极其关键的区分。大多数 RAG 实现仅停留在检索阶段——本质上它们只是昂贵的、带有生成外壳的关键词搜索。而真正的上下文增强是一个完全不同的设计目标。

核心区别

  • 搜索型 RAG 回答的是:“哪些分块与这个查询最相似?”

  • 增强型 RAG 回答的是:“模型需要知道什么才能对这个查询进行深度推理?”

    这两个问题的答案往往截然不同。


是什么让 RAG 真正增强了推理能力?

检索应该服务于推理任务,而非查询字符串。

例如,用户询问“我们是否应该将鉴权服务迁移到 OAuth 2.0?”时,他们需要的不是 OAuth 2.0 的定义,而是:内部架构文档、历史事故报告、当前的鉴权实现,以及关于迁移风险的外部资料。查询逻辑与信息需求是两回事。

这指向了几个核心的设计转变:

1. 检索前的查询拆解 (Query Decomposition)

将复杂问题拆分为子问题,分别检索,最后汇总。对复合问题进行单一嵌入(Embedding)会稀释信号,导致各部分检索到的分块都很平庸。

“我们要迁移到 OAuth 2.0 吗?”

↓ 拆解

  • “我们当前的鉴权实现是怎样的?”
  • “OAuth 2.0 迁移有哪些已知风险?”
  • “我们的鉴权服务有哪些依赖项?”

每个子问题都能检索到极具针对性的上下文,LLM 随后对这些信息的合集进行推理。

2. 多阶段检索管道,而非单一查找

成熟的设计会将检索视为多层过滤的过程,而不仅仅是一次向量搜索:

  1. 初始向量搜索(侧重召回率,高 Top-K)。
  2. 元数据过滤(按文档类型、时效性、领域缩小范围)。
  3. 重排序 Reranking(侧重精度,使用 Cross-Encoder)。
  4. 父块扩展(从小到大的检索策略)。
  5. 上下文组装(去重、排序、修剪以适配窗口)。

3. 将结构化关系引入上下文

如果你的知识库包含关系(如:微调服务依赖某个库、某项政策引用了某条法规),扁平的分块检索会破坏这些信号。图增强 RAG (GraphRAG) 能够保留这些关系:检索时不仅带回匹配的节点,还带回它的邻居节点,赋予模型单纯靠分块无法重构的关系背景。


值得考虑的上下文增强模式

  • 假设性文档嵌入 (.):不直接嵌入原始查询,而是让 LLM 生成一个“假设的理想答案”,并以此进行检索。假设性答案与索引文档处于相同的语义空间,检索更加锐利。

  • 索引时的上下文增强 (Contextual Chunk Enrichment):在嵌入之前,为每个分块添加其所属文档的摘要前缀。

    “本分块来自‘鉴权服务设计文档’第 3 节(令牌生命周期)。该文档涵盖了支付平台的整体鉴权架构。”

    [分块文本内容]

    这显著降低了检索失败率,因为嵌入捕捉到了文档级的背景,而不仅仅是局部的文本。

  • 代理式检索 (Agentic Retrieval):让模型拥有检索工具并可以迭代调用。模型检索、阅读、判断还缺什么、再次检索。虽然成本较高,但能处理初始需求不明确的复杂推理任务。

  • 知识图谱 + 向量混合模式:用向量搜索解决语义相似性,用图遍历解决关系上下文。例如:查询某个 API 接口,向量搜索找到文档,图遍历则带回它所属的服务、依赖项及已知故障模式——这些是仅靠 Embedding 永远无法完整获取的信息。


持续追问的设计准则

在做每一次检索决策时,请询问:“这是在给模型提供推理所需的弹药,还是仅仅提供了与查询文字相似的文本?”

如果你的检索工作做得出色,LLM 回答的质量应该明显高于检索到的分块本身——因为模型是在合成与推理上下文,而不仅仅是提取和改写。

真正优秀的系统将 LLM 视为需要高质量素材的“推理引擎”,而不是一个需要被指向相关段落的“文本生成器”。

这是一个非常有深度且前瞻性的工程综述。它标志着 RAG 已经从早期的“玩具”阶段进化到了上下文工程(Context Engineering)的专业阶段。

以下是为你翻译的内容,我采用了一套更符合 2026 年技术趋势的中文术语体系:


8. 2.0 时代的 RAG:从“检索”进化到“上下文工程”

过去一年,社区的认知和实践已经发生了质的飞跃。以下是目前工程界的核心共识:

1. 范式转移:RAG → 上下文工程 (Context Engineering)

2025 年下半年以来,最热门的技术方向已变为:针对不同任务和时刻,动态且智能地组装最有效的上下文——这被称为上下文工程

  • 重新定义:RAG 自身现在被视为“上下文工程”在领域知识管理中的早期实践。
  • 本质:检索只是管理“模型所见内容”这一更广泛学科中的工具之一,它不是目的,而是一种手段。检索已成为更广泛推理循环中的一个步骤,智能体在此循环中跨数据和工具动态地编写、压缩、隔离和选择上下文。

2. 社区公认的进化曲线

RAG 已经历了三代演进:

  • 原生 RAG (2020–2023):线性的“索引-检索-生成”管道,固定长度分块,直接拼接。
  • 高级 RAG (2023–2024):增加了查询扩展、重排序(Reranking)和混合检索。
  • 智能体 RAG (2025 至今):将 RAG 从被动管道升级为主动智能体,能够自主决定是否需要检索,甚至进行多步规划。

现状:最初的原生实现已基本退出舞台,但其衍生技术正在蓬勃发展。

3. 领先的结构化模式:GraphRAG

GraphRAG 用知识图谱索引取代了扁平的分块检索。

  • 索引阶段:系统提取三元组(头、关系、尾),并通过图聚类生成社区摘要。
  • 查询阶段:通过局部或全局图检索提供结构化证据,显著增强了多跳关联推理能力。
  • 解决痛点:标准 RAG 擅长精确定位事实,但在回答“这个项目呈现出哪些主题?”等全局性问题时表现乏力。图谱方法能通过关系链实现主题级的摘要和溯源。
  • 权衡:GraphRAG 的提取成本是基础 RAG 的 3-5 倍,且需要大量的领域调优。

4. 推理模式:智能体化检索 (Agentic Retrieval)

不再是固定的单次查找,而是由自主智能体规划多个检索步骤:

  • 多步规划:智能体选择工具、反思中间答案,并针对复杂任务(如跨系统的合规性检查)调整策略。
  • Self-RAG (自反思 RAG):引入了能够自我评估的模型——决定何时检索、评估内容的关联性、并在响应前对输出进行自我批判,从而通过条件化检索大幅减少幻觉。

5. 正在普及的核心工程实践

  • “混合检索 + 重排序”成为新基线:两阶段混合检索配合 Cross-Encoder 重排序器,可将 Recall@5 从单混合检索的 0.695 提升至 0.816,这 17% 的提升直接转化为下游的回答忠实度。
  • 索引时的上下文分块增强:为每个分块附带简短的文档级摘要。这解决了原生分块最大的弱点——让分块带有全局背景,使模糊查询的检索更精准。
  • 结构化上下文组装:不再将纯文本直接丢进 Prompt,而是将检索到的分块作为“结构化块”传递,让 LLM 能够独立解析每个单元。这确保了 Prompt 的一致性,并简化了 Prompt 工程。
  • 向量与图谱的融合:在企业场景中,用向量检索处理精确查询,用 GraphRAG 进行整体分析,可覆盖 90% 以上的知识问答需求。

6. 关于价值核心的共识

在生产环境中做对 RAG,本质上是一个上下文基础设施问题

  • 底层决定上层:检索层的可靠性完全取决于底层的知识结构。
  • 结论:真正的杠杆不在于检索算法本身,而在于索引的设计、结构的构造以及关系的保存

目前最前瞻的视角是:不要将你的知识库视为文档仓库,而要将其视为“推理底座(Reasoning Substrate)”——其结构应被设计为易于模型“导航(Navigate)”,而不仅仅是“搜索(Search)”。


Ethan,看到这里你是否发现,你之前想的“只检索 Q 而返回 QA”其实就是这种“上下文工程”的一个微小而精准的起点?